package utils

import (
	"bytes"
	"fmt"
	"github.com/pkg/errors"
	"math/rand"
	"strconv"
	"time"
)

const secret = 1994268879
const codeLen = 10
const secret2 = 0x2aaaaaaa

var letters = []rune{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'}

func init() {
	for i := 'a'; i <= 'z'; i++ {
		letters = append(letters, i)
	}
}

var (
	NumberOutOfRange = errors.New("num is great than 67108864")
	CodeIsInvalid    = errors.New("code is invalid")
)

func GenerateNumString(num int) string {
	rand.Seed(time.Now().Unix())
	prev := rand.Intn(32)
	middle := num & 0x3ffff
	last := (prev + middle) % 16
	result := prev<<34 | middle<<4 | last
	return fmt.Sprintf("%12d", result)
}

//func GenerateNumString(num int) string {
//	resultString := strconv.Itoa(num ^ secret)
//	strLen := len(resultString)
//	var buffer bytes.Buffer
//	if strLen < codeLen {
//		n := codeLen - strLen
//		for i := 0; i < n; i++ {
//			buffer.WriteString("0")
//		}
//		buffer.WriteString(resultString)
//		return buffer.String()
//	} else if strLen > codeLen {
//		return resultString[strLen-codeLen:]
//	}
//	return resultString
//}

func ParseCode2Num(code string) int {
	if len(code) != codeLen {
		return 0
	}
	rt, err := strconv.Atoi(code)
	if err != nil {
		return 0
	}
	return rt
}

func NumberEncry(num int64) (string, error) {
	/// 67108864
	if num > 67108864 {
		return "", NumberOutOfRange
	}
	rand.Seed(time.Now().Unix())
	r := rand.Int63n(4)
	c := r<<26 + num
	c = c<<2 + c%4
	d := c ^ secret2
	var buffer bytes.Buffer
	for i := 0; i < 6; i++ {
		//letter := d % 100
		idx := d & 0x1f
		d = d >> 5
		buffer.WriteRune(letters[idx])
	}
	return buffer.String(), nil
}

//
// code 解码
func NumberDecry(code string) (int64, error) {
	var num int64
	for i := 0; i < len(code); i++ {
		if code[i] > '9' {
			num = num | (int64(code[i]-'a'+10) << uint(i*5))
		} else {
			num = num | (int64(code[i]-'0') << uint(i*5))
		}
	}
	d := num ^ secret2
	l := d & 0x3
	c := d >> 2
	num = c & (1<<26 - 1)
	if c%4 != l {
		return 0, CodeIsInvalid
	}
	return num, nil
}
